home *** CD-ROM | disk | FTP | other *** search
/ APDL Other Worlds / APDL Other Worlds Collection.iso / SF3000 / Extras / CBlibrary / c / RoundRobin < prev    next >
Encoding:
Text File  |  2003-10-16  |  7.1 KB  |  263 lines

  1. /*
  2.  * CBLibrary - RoundRobin
  3.  * Copyright (C) 2003  Chris Bazley
  4.  *
  5.  * This library is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU Lesser General Public
  7.  * License as published by the Free Software Foundation; either
  8.  * version 2.1 of the License, or (at your option) any later version.
  9.  *
  10.  * This library is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  * Lesser General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU Lesser General Public
  16.  * License along with this library; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  */
  19.  
  20. /* Round-robin system for multiple threads that require null polls */
  21.  
  22. /* ANSI library files */
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <stdbool.h>
  26. #ifndef NDEBUG
  27. #include <stdio.h>
  28. #endif
  29. #include <assert.h>
  30.  
  31. /* RISC OS library files */
  32. #include "kernel.h"
  33. #include "wimp.h"
  34. #include "event.h"
  35. #include "toolbox.h"
  36.  
  37. /* My library files */
  38. #include "Macros.h"
  39. #include "err.h"
  40. #include "NullPoll.h"
  41. #include "timer.h"
  42. #include "RoundRobin.h"
  43.  
  44. typedef struct _RoundRobin_record {
  45.   RoundRobinHandler *handler; /* == NULL marks free blocks */
  46.   void *handle;
  47. } RoundRobin_record;
  48.  
  49. extern _kernel_oserror shared_err_block;
  50.  
  51. static int suspended;
  52. static RoundRobin_record *threads_array;
  53. static unsigned int threads_array_len, thread_to_call, num_threads, maxtime;
  54. static volatile bool time_up = true;
  55.  
  56. /* ----------------------------------------------------------------------- */
  57. /*                       Function prototypes                               */
  58.  
  59. static WimpEventHandler _RoundRobin_handler;
  60. static void ensure_timer_removed(void);
  61.  
  62. /* ----------------------------------------------------------------------- */
  63. /*                         Public functions                                */
  64.  
  65. _kernel_oserror *RoundRobin_initialise(unsigned int time)
  66. {
  67.   THROW(event_register_wimp_handler(-1, Wimp_ENull, _RoundRobin_handler, NULL))
  68.   thread_to_call = 0;
  69.   threads_array_len = 0;
  70.   threads_array = NULL;
  71.   suspended = 0;
  72.   maxtime = time;
  73.   atexit(ensure_timer_removed);
  74.   return NULL;
  75. }
  76.  
  77. /* ----------------------------------------------------------------------- */
  78.  
  79. #ifdef INCLUDE_FINALISATION_CODE
  80. _kernel_oserror *RoundRobin_finalise(void)
  81. {
  82.   THROW(event_deregister_wimp_handler(-1, Wimp_ENull, _RoundRobin_handler, NULL))
  83.   free(threads_array);
  84.   threads_array_len = 0;
  85.   if(num_threads > 0 && suspended != 0)
  86.     nullpoll_deregister();
  87.  
  88.   return NULL;
  89. }
  90. #endif
  91.  
  92. /* ----------------------------------------------------------------------- */
  93.  
  94. _kernel_oserror *RoundRobin_register(RoundRobinHandler *handler, void *handle)
  95. {
  96.   /* Add record to threads data */
  97.   RoundRobin_record *write_data = NULL;
  98.  
  99.   if(threads_array != NULL) {
  100.     /* Search for a free RoundRobin_record block in array */
  101.     unsigned int i;
  102.     for(i = 0; i < threads_array_len && write_data == NULL; i++) {
  103.       if(threads_array[i].handler == NULL)
  104.         write_data = &(threads_array[i]); /* found one */
  105.     }
  106.   }
  107.  
  108.   if(write_data == NULL) {
  109.     /* Create/Extend array of RoundRobin_record blocks */
  110.     RoundRobin_record *new_data = realloc(threads_array, sizeof(RoundRobin_record)*(threads_array_len+1));
  111.     if(new_data == NULL) {
  112.       WRITE_GERR(shared_err_block, "NoMem");
  113.       return &shared_err_block;
  114.     }
  115.     threads_array = new_data;
  116.     write_data = &(threads_array[threads_array_len]);
  117.     threads_array_len++;
  118.     write_data->handler = NULL; /* mark new (end) block as free */
  119.   }
  120.  
  121.   if(num_threads == 0 && suspended == 0)
  122.     nullpoll_register(); /* We have first client */
  123.   num_threads++;
  124.  
  125.   /* Fill out fields of RoundRobin_record block */
  126.   write_data->handler = handler;
  127.   write_data->handle = handle;
  128.  
  129.   return NULL; /* success */
  130. }
  131.  
  132. /* ----------------------------------------------------------------------- */
  133.  
  134. void RoundRobin_deregister(RoundRobinHandler *handler, void *handle)
  135. {
  136.   RoundRobin_record *write_data = NULL;
  137.  
  138.   /* Search for the specified thread data */
  139.   {
  140.     unsigned int i;
  141.     for(i = 0; i < threads_array_len && write_data == NULL; i++) {
  142.       if(threads_array[i].handler == handler
  143.       && threads_array[i].handle == handle)
  144.         write_data = &(threads_array[i]); /* found it */
  145.     }
  146.   }
  147.   assert(write_data != NULL);
  148.   if(write_data == NULL)
  149.     return; /* bad deregistration */
  150.  
  151.   if(num_threads == 1 && suspended == 0)
  152.     nullpoll_deregister(); /* We have run out of clients */
  153.   num_threads--;
  154.  
  155.   write_data->handler = NULL; /* mark slot as free */
  156. }
  157.  
  158. /* ----------------------------------------------------------------------- */
  159.  
  160. void RoundRobin_suspend(void)
  161. {  
  162.   if(suspended == 0 && num_threads > 0)
  163.     nullpoll_deregister();
  164.   suspended++;
  165. }
  166.  
  167. /* ----------------------------------------------------------------------- */
  168.  
  169. void RoundRobin_resume(void)
  170. {
  171.   /* Can't resume if not suspended! */
  172.   assert(suspended >= 1);
  173.   if(suspended < 1)
  174.     return; /* not suspended */
  175.  
  176.   if(suspended == 1 && num_threads > 0)
  177.     nullpoll_register();
  178.  
  179.   suspended--;
  180. }
  181.  
  182.  
  183. /* ----------------------------------------------------------------------- */
  184. /*                         Private functions                               */
  185.  
  186. static int _RoundRobin_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  187. {
  188.   NOT_USED(event_code)
  189.   NOT_USED(event)
  190.   NOT_USED(id_block)
  191.   NOT_USED(handle)
  192. #ifndef NDEBUG
  193.   int num_this_poll = 0;
  194. #endif
  195.  
  196.   if(threads_array == NULL || suspended != 0)
  197.     return 0; /* nothing to do - pass event on */
  198.     
  199.   {
  200.     _kernel_oserror *err;
  201.     time_up = false;
  202.     err = timer_register(&time_up, maxtime);
  203.     if(err != NULL) {
  204.       time_up = true; /* could not set up timer event */
  205.       err_check_rep(err);
  206.     }
  207.   }
  208.  
  209. #ifndef NDEBUG
  210.   _kernel_oscli("report Entering RoundRobin null event dispatcher");
  211. #endif
  212.  
  213.   do {
  214.     RoundRobin_record *block;
  215.  
  216.     if(thread_to_call >= threads_array_len)
  217.       thread_to_call = 0;
  218.  
  219.     block = &threads_array[thread_to_call];
  220. #ifndef NDEBUG
  221.     {
  222.       char string[255];
  223.       sprintf(string, "report Thread record %d at %p", thread_to_call, block);
  224.       _kernel_oscli(string);
  225.     }
  226. #endif
  227.  
  228.     if(block->handler != NULL) {
  229. #ifndef NDEBUG
  230.       char string[255];
  231.       sprintf(string, "report Calling handler %p with handle %p", block->handler, block->handle);
  232.       _kernel_oscli(string);
  233.       num_this_poll++;
  234. #endif
  235.       block->handler(block->handle, &time_up);
  236.     }
  237.  
  238.     thread_to_call++;
  239.   } while (!time_up);
  240.   ensure_timer_removed(); /* in case OS_CallAfter event still pending */
  241.  
  242. #ifndef NDEBUG
  243.   {
  244.     char string[255];
  245.     sprintf(string, "report Handlers called this poll: %d", num_this_poll);
  246.     _kernel_oscli(string);
  247.   }
  248. #endif
  249.   return 0; /* pass event on */
  250. }
  251.  
  252. /* ----------------------------------------------------------------------- */
  253.  
  254. static void ensure_timer_removed(void)
  255. {
  256.   /* Last-ditch effort to remove OS_CallAfter routine, if still pending */
  257.   if(!time_up) {
  258.     timer_deregister(&time_up);
  259.     time_up = true;
  260.   }
  261.   /* (suppress errors, as event may occur between check and removal) */
  262. }
  263.